package com.coco.homework.engine;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.coco.homework.Context;
import com.coco.homework.RomanNumber;
import com.coco.homework.convert.InputParse;
import com.coco.homework.rules.IRomanNumberRule;
import com.coco.homework.rules.RomanNumberCRule;
import com.coco.homework.rules.RomanNumberDRule;
import com.coco.homework.rules.RomanNumberIRule;
import com.coco.homework.rules.RomanNumberLRule;
import com.coco.homework.rules.RomanNumberMRule;
import com.coco.homework.rules.RomanNumberVRule;
import com.coco.homework.rules.RomanNumberXRule;

/**
 * <b>处理引擎,每次只处理一个字符,如果需要计算后续字符,在目标的Rule中进行计算修改</b>
 * <p>1、注册默认规则</p>
 * <p>2、记录处理过程中的用户输入和RomanNumber映射</p>
 * <p>3、记录处理过程中的用户输入的本地变量</p>
 * @author Yanphu
 *
 */
public class RomanNumberEngine {

	private Map<RomanNumber, IRomanNumberRule> rules = new HashMap<>();

	private Map<String, RomanNumber> mappingContext = new HashMap<>();

	private Map<String, Float> localContext = new HashMap<>();

	public RomanNumberEngine() {
		registerDefaultRule();
		
	}

	public void registerMappingContext(String key, RomanNumber value) {
		mappingContext.put(key, value);
	}

	public void registerLocalContext(String key, Float value) {
		localContext.put(key, value);
	}

	public void registerRule(IRomanNumberRule rule) {
		rules.put(rule.getCanHandlerNumber(), rule);
	}

	public Integer exec(String str) {
		Context context = new Context(str);
		while (!context.eof()) {
			char ch = context.getCurrentChar();
			RomanNumber romanNumber = RomanNumber.of(ch);
			IRomanNumberRule rule = rules.get(romanNumber);
			rule.exec(context);
			context.skipCharCount(1);
		}
		return context.getValue();
	}

	public String parse(String line) {
		InputParse parse = InputParse.parse(line);
		String keyWordStrings, result = null;
		StringBuilder stringBuilder;
		String[] keyWords;
		List<String> localStrings;
		Integer execVal;
		Object[] objs = parse.getObjs();
		Float tagretVal;
		switch (parse.getInputs()) {
		case IS_INPUT:
			this.registerMappingContext(String.valueOf(objs[0]), RomanNumber.of(String.valueOf(objs[1])));
			break;
		case EXEC_INPUT:
			keyWordStrings = String.valueOf(objs[0]);
			Integer value = Integer.valueOf(String.valueOf(objs[1]));
			keyWords = keyWordStrings.split(" |,|\\?|\\.");
			String storeStr = null;
			stringBuilder = new StringBuilder();
			for (String keyWord : keyWords) {
				RomanNumber romanNumber = this.mappingContext.get(keyWord);
				if (null == romanNumber) {
					storeStr = keyWord;
				} else {
					stringBuilder.append(romanNumber.name());
				}
			}
			if (null != storeStr) {
				execVal = exec(stringBuilder.toString());
				registerLocalContext(storeStr, (float) value / execVal);
			}
			break;
		case HOW_MANY_INPUT:
			keyWordStrings = String.valueOf(objs[0]);
			keyWords = keyWordStrings.split(" |,|\\?|\\.");

			localStrings = new ArrayList<>();
			stringBuilder = new StringBuilder();
			for (String keyWord : keyWords) {
				RomanNumber romanNumber = this.mappingContext.get(keyWord);
				if (null == romanNumber) {
					localStrings.add(keyWord);
				} else {
					stringBuilder.append(romanNumber.name());
				}
			}
			execVal = exec(stringBuilder.toString());
			tagretVal = (float) execVal;
			for (String localString : localStrings) {
				Float localValue = localContext.get(localString);
				if (null != localValue) {
					tagretVal *= localValue;
				}
			}
			result = MessageFormat.format(parse.getInputs().getOutput(), keyWordStrings, tagretVal);
			break;
		case HOW_MUCH_INPUT:
			keyWordStrings = String.valueOf(objs[0]);
			keyWords = keyWordStrings.split(" |,|\\?|\\.");

			localStrings = new ArrayList<>();
			stringBuilder = new StringBuilder();
			for (String keyWord : keyWords) {
				RomanNumber romanNumber = this.mappingContext.get(keyWord);
				if (null == romanNumber) {
					localStrings.add(keyWord);
				} else {
					stringBuilder.append(romanNumber.name());
				}
			}
			execVal = exec(stringBuilder.toString());
			tagretVal = (float) execVal;
			for (String localString : localStrings) {
				Float localValue = localContext.get(localString);
				if (null != localValue) {
					tagretVal *= localValue;
				}
			}
			result = MessageFormat.format(parse.getInputs().getOutput(), keyWordStrings, execVal);
			break;
		case UNKONWS_INPUT:
			result = parse.getInputs().getOutput();
			break;
		default:
			break;
		}
		return result;
	}

	private void registerDefaultRule() {
		registerRule(new RomanNumberCRule());
		registerRule(new RomanNumberDRule());
		registerRule(new RomanNumberIRule());
		registerRule(new RomanNumberLRule());
		registerRule(new RomanNumberMRule());
		registerRule(new RomanNumberVRule());
		registerRule(new RomanNumberXRule());		
	}
	
	@Override
	public String toString() {
		return "RomanNumberEngine [rules=" + rules + ", mappingContext=" + mappingContext + ", localContext="
				+ localContext + "]";
	}
}
